package org.cli.admin.cli;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import jline.ConsoleReader;

import org.cli.admin.Main;
import org.cli.admin.cli.printer.ConsolePrinter;
import org.cli.admin.command.ICommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.beust.jcommander.JCommander;
import com.beust.jcommander.MissingCommandException;
import com.beust.jcommander.ParameterException;
import com.google.inject.Inject;

class CLIListener implements Runnable {

	private static final Logger logger = LoggerFactory.getLogger(CLIListener.class);
			
	private	static final String COMMAND_PROMPT = "> ";
	
	private Executor commandExecutor;
	
	private final AtomicBoolean isWorking;

	@Inject
	private JCommander commander;

	@Inject
	private ConsoleReader reader;
	
	@Inject
	private ConsolePrinter printer;

	public CLIListener() {
		commandExecutor = Executors.newCachedThreadPool();
		isWorking = new AtomicBoolean();
	}

	public void run() {
		// Detection for single command launch mode
		if (Main.isSingleCommandMode()) {
			execute(Main.getCommandLine());

			while (getIsWorking().get()) {
				try {
					TimeUnit.MILLISECONDS.sleep(100L);
				} catch (InterruptedException e) {
					logger.error("Error", e);
				}
			}
			logger.info("Command executed. Exiting...");
			System.exit(0);
		}

		printer.printEnterCommand();
		
		while (true) {
			
			try {
				String commandLine = reader.readLine(COMMAND_PROMPT);
				if (commandLine == null) {
					return;
				}

				if (commandLine.isEmpty()) {
					printer.printEnterCommand();
					continue;
				}

				execute(commandLine);
			} catch (IOException e) {
				logger.error("Error", e);
				System.exit(-1);
			}
		}

	}

	private void execute(String commandLine) {
		String sCommand = "";
		try {
			String[] split = commandLine.split(" ");
			if (split.length > 0) {
				sCommand = split[0];
				commander.parse(split);

				String parsedCommand = commander.getParsedCommand();
				List<Object> commands = commander.getCommands().get(parsedCommand).getObjects();
				for (final Object command : commands) {

					if (command instanceof ICommand) {
						getIsWorking().set(true);

						// Concurrent command execution model
						commandExecutor.execute(new Runnable() {
							public void run() {
								ICommand aCommand = (ICommand) command;
								try {
									aCommand.execute();
								} catch (Exception e) {
									logger.error("Error", e);
								} finally {
									if (!Main.isSingleCommandMode()) {
										printer.printEnterCommand();
										System.out.print(COMMAND_PROMPT);
										System.out.flush();
									}
									getIsWorking().set(false);
								}
							}
						});
					}
				}
			} else {
				logger.info("command not found");
			}
		} catch (MissingCommandException ignored) {
			logger.info(String.format("%s: command not found", sCommand));
			printer.printEnterCommand();
		} catch (ParameterException mce) {
			logger.info(String.format("%s: invalid parameters passed [%s]. See usage example 'usage %s'", sCommand, mce.getMessage(), sCommand));
			printer.printEnterCommand();
		} catch (Exception ignored) {
			logger.info(String.format("%s: invalid parameters passed. See usage example 'usage %s'", sCommand, sCommand));
			printer.printEnterCommand();
		}
	}

	

	public AtomicBoolean getIsWorking() {
		return isWorking;
	}
}